﻿using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Syngine;
using Syngine.Audio;
using Syngine.Components.Animations;
using Syngine.Input;
#if !(WINDOWS_PHONE || ANDROID)
using Syngine.Input.Mouse;
#endif
using Syngine.Input.Keyboard;
using Syngine.Physics;
using Syngine.Physics.Components;

namespace BenchMark.PlatformerGame
{
	public class Player : PlayerSprite
	{
		private bool _isJumping;
		private const float _maxAcceleration = 1.75f;
		private const float _accelerationIncrement = 0.1f;
		private float _currentAcceleration;
		private SpriteSheetAnimation _leftAnimation;
		private SpriteSheetAnimation _rightAnimation;
		private SpriteSheetAnimation _jumpleftAnimation;
		private SpriteSheetAnimation _jumprightAnimation;
		private readonly IAudioManager _audio = new AudioManager();
		private SoundEffect _engineSound;

		protected override void InitializeInput()
		{
			Input.If(i => i.WasPressed(Keys.Up) || i.WasPressed(Keys.W)).Call(JumpCallback);
			Input.If(i => i.IsDown(Keys.Left) || i.IsDown(Keys.A)).Call(MoveLeftCallback);
			Input.If(i => i.IsDown(Keys.Right) || i.IsDown(Keys.D)).Call(MoveRightCallback);
			Input.If(i => i.WasReleased(Keys.Left) || i.WasReleased(Keys.A)).Call(StopMovingLeftCallback);
			Input.If(i => i.WasReleased(Keys.Right) || i.WasReleased(Keys.D)).Call(StopMovingRightCallback);
#if !(WINDOWS_PHONE || ANDROID)
			Input.If(i => i.IsSelected(Bounds)).Call(JumpCallback);
#endif
		}

		public override void LoadContent(LoadContext context)
		{
			base.LoadContent(context);

			Body = BodyFactory.CreateRectangle(WorldContext.World, ConvertUnits.ToSimUnits(32), ConvertUnits.ToSimUnits(32), 1f);
			Body.Mass = 10.0f;
			Body.BodyType = BodyType.Dynamic;
			Body.CollidesWith = Category.All;
			Body.Friction = 1.0f;
			Body.FixedRotation = true;
			Body.Position = new Vector2(0.1f);
			Body.OnCollision += OnCollision;

			_leftAnimation = new SpriteSheetAnimation(Texture, new Vector2(3, 1), 32, 32, 0.50f, 0, 3);
			_rightAnimation = new SpriteSheetAnimation(Texture, new Vector2(3, 2), 32, 32, 0.50f, 0, 3);
			_jumpleftAnimation = new SpriteSheetAnimation(Texture, new Vector2(0, 1), 32, 32, 0.50f, 0, 0);
			_jumprightAnimation = new SpriteSheetAnimation(Texture, new Vector2(0, 2), 32, 32, 0.50f, 0, 0);

			_leftAnimation.LoadContent(context);
			_rightAnimation.LoadContent(context);
			_jumpleftAnimation.LoadContent(context);
			_jumprightAnimation.LoadContent(context);

			AnimationPlayer.Play(_rightAnimation);
			AnimationPlayer.Pause();

			_engineSound = context.Content.Load<SoundEffect>("Audio\\Waves\\hyperspace_activate");

			base.LoadContent(context);
		}

		protected override Texture2D GetAsset(LoadContext context)
		{
			return context.Load<Texture2D>("Sprites\\Player");
		}

		private bool OnCollision(Fixture fixtureA, Fixture fixtureB, Contact contact)
		{
			if ((contact.Manifold.LocalNormal.Y <= -1.0f || contact.Manifold.LocalNormal.Y >= 1.0f) && Body.BodyId == fixtureA.Body.BodyId)
			{
				_isJumping = false;
			}

			return true;
		}

		public override void UnloadContent()
		{
		}

		protected override void UpdateCore(UpdateContext context)
		{
			var position = DisplayPosition;
			GameContext.Camera.Focus = position;
			_leftAnimation.Position = position;
			_rightAnimation.Position = position;
			_jumpleftAnimation.Position = position;
			_jumprightAnimation.Position = position;

			_leftAnimation.Update(context);
			_rightAnimation.Update(context);
			_jumpleftAnimation.Update(context);
			_jumprightAnimation.Update(context);

			base.UpdateCore(context);
		}

		private void JumpCallback(InputCallbackContext c)
		{
			if (!_isJumping)
			{
				_isJumping = true;
				_audio.Play(_engineSound);

				ApplyForce(new Vector2(0, -32));
			}
		}

		private void MoveLeftCallback(InputCallbackContext context)
		{
			if (!_isJumping)
			{
				AnimationPlayer.Play(_leftAnimation);
			}
			else
			{
				AnimationPlayer.Play(_jumpleftAnimation);
			}

			if (_currentAcceleration > -_maxAcceleration)
			{
				_currentAcceleration -= _accelerationIncrement;
			}

			Body.LinearVelocity = new Vector2(_currentAcceleration, Body.LinearVelocity.Y);
		}

		private void MoveRightCallback(InputCallbackContext context)
		{
			if (!_isJumping)
			{
				AnimationPlayer.Play(_rightAnimation);
			}
			else
			{
				AnimationPlayer.Play(_jumprightAnimation);
			}

			if (_currentAcceleration < _maxAcceleration)
			{
				_currentAcceleration += _accelerationIncrement;
			}

			Body.LinearVelocity = new Vector2(_currentAcceleration, Body.LinearVelocity.Y);
		}

		private void StopMovingLeftCallback(InputCallbackContext context)
		{
			Body.LinearVelocity = new Vector2(_currentAcceleration / 1.5f, Body.LinearVelocity.Y);
			
			_currentAcceleration = 0;

			if (!_isJumping)
			{
				AnimationPlayer.Stop();
				AnimationPlayer.Pause();
			}
		}

		private void StopMovingRightCallback(InputCallbackContext context)
		{
			Body.LinearVelocity = new Vector2(_currentAcceleration / 1.5f, Body.LinearVelocity.Y);

			_currentAcceleration = 0;

			if (!_isJumping)
			{
				AnimationPlayer.Stop();
				AnimationPlayer.Pause();
			}
		}
	}
}
